#ifndef __C_SPARSE_MATRIX_CSR1__h__
#define __C_SPARSE_MATRIX_CSR1__h__

#include "CSparseMatrixCSR.hpp"

#pragma warning(disable : 4996)



using namespace std;


template<typename T>
class CSparseMatrixCSR1
{
public:
	enum Exeptions {fileNotFound};
	
	/*********************constructors*****************************/
	
	CSparseMatrixCSR1(const char * fileName);

	//Destructor
	~CSparseMatrixCSR1();
	

	
	
	void printMToFile(const char * fileName);
	void printMToFile_L_U(const char * fileName);

private:


	T* _values;
	//oszlopindexek
	unsigned * _columnId;
	//number of the non-zero elements
	unsigned _nonZero;
	//row number
	unsigned _n;
	//column number
	unsigned _m;
	bool _isSymmetric;


	vector<string> _fileData;
};

template<typename T>
CSparseMatrixCSR1<T>::CSparseMatrixCSR1(const char * fileName)
{
	
	ifstream inF(fileName);

	if(inF==0)
	{
		cout<<"File not found!"<<endl;
		throw fileNotFound;
	}

	string line;
	getline(inF,line);

	_fileData.push_back(line);
	//get matrix propereties
	vector<string> res;
	boost::algorithm::split(res,line,boost::is_any_of(" "),boost::algorithm::token_compress_on);

	if(res[1]!="matrix")
	{
		//error_command_line("This is not a matrix!");
		exit(1);
	}
	
	//is the matrix symmetric or general
	this->_isSymmetric=(res[4]=="symmetric");


	//read comments
	getline(inF,line);
	_fileData.push_back(line);
	while(line[0]=='%')
	{
		getline(inF,line);
		_fileData.push_back(line);
	}


	vector<string> res2;
	boost::algorithm::split(res2,line,boost::is_any_of(" "),boost::algorithm::token_compress_on);
	_n=atoi(res2[0].c_str());
	_m=atoi(res2[1].c_str());
	_nonZero=atoi(res2[2].c_str());
	

	//allocate the memory
	_columnId=new unsigned[_nonZero];
	
	_values=new T[_nonZero];
	
	//CSR format
	unsigned * columnPtr=new unsigned[_m+1];
	columnPtr[0]=0;
	unsigned * rowId=new unsigned[_nonZero];
	T* values=new T[_nonZero];
	
	//temporary variables
	int i,j;
	T val;
	//read the matrix in CCS format

	int l=0;
	inF>>i;
	inF>>j;
	inF>>val;
	
	for(unsigned k=0;k<_m;++k)
	{
		while(j==k+1)
		{
			values[l]=val;
			rowId[l]=--i;
			++l;
		
			inF>>i;
			inF>>j;
			inF>>val;
			if(inF.eof())
			{
				j=0;
			}

		}
		columnPtr[k+1]=l;
	}

	inF.close();

	//convert to CSR

	for(int i=0;i<_nonZero;++i)
	{
		_columnId[i]=0.0;
	}

	for(unsigned i=0;i<_nonZero;++i)
	{
		++_columnId[rowId[i]];
	}

	_columnId[0]+=_n-1;
	for(unsigned i=1;i<_n;++i)
	{
		_columnId[i]+=_columnId[i-1]-1;//because diagonal element
	}


	//collect the elements
	vector<unsigned> map(_n,0);

	unsigned valuesInd=0;

	//iterate on columns
	for(unsigned i=0;i<_n;++i)
	{
		unsigned k;
		for(unsigned j=columnPtr[i];j<columnPtr[i+1];++j)
		{
			//where we insert the element
			if(rowId[valuesInd]>0)
			{
				k=_columnId[rowId[valuesInd]-1]+map[rowId[valuesInd]];
			}
			else
			{
				k=_n+map[rowId[valuesInd]];
			}
			
			//is diagonal element
			if(rowId[valuesInd]!=i)
			{
				_values[k]=values[valuesInd];
				++map[rowId[valuesInd]];
				_columnId[k]=i;
			}
			else
			{
				_values[rowId[valuesInd]]=values[valuesInd];
			}

			//valus array index
			++valuesInd;
		}
	}


	/*if(_isSymmetric)
	{
		ofstream sym("symmetric.mtx");
		sym.precision(30);
		sym<<"%%MatrixMarket matrix coordinaterealNumbergeneral"<<endl;
		sym<<"%-------------------------------------------------------------------------------"<<endl;
		sym<<_n<<" "<<_n<<" "<<2*_nonZero-_n<<endl;
		for(uint i=0;i<_n;++i)
		{
			
			for(uint j=_rowPtr[i];j<_rowPtr[i+1];++j)
			{
				sym<<_columnId[j]+1<<" "<<i+1<<" "<<_values[j]<<endl;
			}
			for(uint j=columnPtr[i];j<columnPtr[i+1];++j)
			{
				if(rowId[j]!=i)
				{
					sym<<rowId[j]+1<<" "<<i+1<<" "<<values[j]<<endl;
					//cout<<values[j]<<endl;
				}
			}


		}
		sym.close();
		CSparseMatrixCSR1<T> * tmp=new CSparseMatrixCSR1<T>("symmetric.mtx");
		delete [] _rowPtr;
		delete [] _columnId;
		delete [] _values;
		_rowPtr=tmp->_rowPtr;
		_columnId=tmp->_columnId;
		_values=tmp->_values;
		_n=tmp->_n;
		_m=tmp->_m;
		_nonZero=tmp->_nonZero;
		_isSymmetric=tmp->_isSymmetric;
		_tmp=0;
	}*/



	delete [] values;
	delete []  rowId;
	delete [] columnPtr;
}


template<typename T>
CSparseMatrixCSR1<T>::~CSparseMatrixCSR1()
{
	delete [] _values;
	delete [] _columnId;
}

template <typename T>
void CSparseMatrixCSR1<T>::printMToFile(const char * fileName)
{
	ofstream out(fileName);
	out<<_fileData[0]<<endl;

	out<<"%CSR 1 Matrix Representation, converted from column format (rowIndex, columnIndex, values):"<<endl;
	
	for(unsigned i=1;i<_fileData.size();++i)
	{
		out<<_fileData[i]<<endl;
	}
	
	for(uint i=0;i<_nonZero;++i)
	{
		out<< boost::lexical_cast<string>(_values[i])<<" ";
	}

	out<<endl;

	for(uint i=0;i<_nonZero;++i)
	{
		out<<_columnId[i]<<" ";
	}

	out<<endl;


	out.close();
	
}

template <typename T>
void CSparseMatrixCSR1<T>::printMToFile_L_U(const char * fileName)
{
	ofstream out(fileName);
	out<<_fileData[0]<<endl;

	out<<"%CSR D + L + U Matrix Representation, converted from column format (rowIndex, columnIndex, values):"<<endl;
	
	for(unsigned i=1;i<_fileData.size();++i)
	{
		out<<_fileData[i]<<endl;
	}
	
	vector<T> L;
	vector<unsigned> L_col_Ind;
	vector<unsigned> L_rowPt(_n-1);
	
	vector<T> U;
	vector<unsigned> U_col_Ind;
	vector<unsigned> U_rowPt(_n-1);


	uint j=_n;
	for(uint i=0;i<_n;++i)
	{
		while(j<_columnId[i])
		{
			if(_columnId[j]>i)
			{
				++U_rowPt[i];
				U_col_Ind.push_back(_columnId[j]);
				U.push_back(_values[j]);
			} else
			{
				++L_rowPt[i-1];
				L_col_Ind.push_back(_columnId[j]);
				L.push_back(_values[j]);
			}
			++j;
		}
	}

	for(uint i=0;i<_n;++i)
	{
		out<<boost::lexical_cast<string>(_values[i])<<" ";
	}

	out<<endl;

	out<<L.size()<<" ";
	for(uint i=0;i<L.size();++i)
	{
		out << boost::lexical_cast<string>(L[i])<<" ";
	}

	out<<endl;

	for(uint i=0;i<L_col_Ind.size();++i)
	{
		out<<L_col_Ind[i]<<" ";
	}

	out<<endl;

	for(uint i=0;i<L_rowPt.size();++i)
	{
		if(i>0)
		{
			L_rowPt[i]+=L_rowPt[i-1];
		}
		out<<L_rowPt[i]<<" ";
	}

	out<<endl;
	out<<U.size()<<" ";
	for(uint i=0;i<U.size();++i)
	{
		out << boost::lexical_cast<string>(U[i])<<" ";
	}

	out<<endl;

	for(uint i=0;i<U_col_Ind.size();++i)
	{
		out<<U_col_Ind[i]<<" ";
	}

	out<<endl;

	for(uint i=0;i<U_rowPt.size();++i)
	{
		if(i>0)
		{
			U_rowPt[i]+=U_rowPt[i-1];
		}
		out<<U_rowPt[i]<<" ";
	}

	out<<endl;


	out.close();
	
}

#endif